home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / BackSpaceViews / DigitalClockView.BackModule / DigitalClockView.m < prev    next >
Text File  |  1995-06-12  |  11KB  |  390 lines

  1. //
  2. // DigitalClockView.m
  3. // rev 1.3
  4. // Now supports BackSpace inspector panels
  5. //
  6. // Matt Pharr- pharr@cs.yale.edu
  7. // NeXTMail welcome
  8. //
  9.  
  10. #import "DigitalClockView.h"
  11. #import <appkit/appkit.h>
  12. #import <defaults/defaults.h>
  13. #import <dpsclient/wraps.h>
  14. #import <libc.h>
  15. #import <stdlib.h>
  16. #import <strings.h>
  17. #import <time.h>
  18.  
  19. #define AM_PM        0
  20. #define MILITARY    1
  21.  
  22. @implementation DigitalClockView
  23.  
  24. /* Fixes the NXImage so that if there isn't room to display it on the
  25.  * screen (i.e. it's too big, or the screen/window space is too small)
  26.  * it still behaves generally nicely.
  27.  */
  28. - fixPosition
  29. {
  30.     /* If we're doing AM/PM time and a leading 0 was taken off the time
  31.      * string that strftime returned, let some of the NXImage go off the
  32.      * edge of the screen before we force it back on. When the leading 0
  33.      * is taken off, we have extra room in currentImage, which is a black
  34.      * area at the right side, so we can let that part go off the screen...
  35.      */
  36.     
  37.     if (militaryTime == NO && didShorten == YES) {
  38.         if (currentLocation.x + .9 * currentSize.width >= bounds.size.width)
  39.         currentLocation.x = bounds.size.width - .9 * currentSize.width;
  40.     }
  41.     else {
  42.         if (currentLocation.x + currentSize.width >= bounds.size.width)
  43.             currentLocation.x = bounds.size.width - currentSize.width;
  44.     }
  45.  
  46.  
  47.     if (currentLocation.x <= bounds.origin.x)
  48.        currentLocation.x = 0;
  49.  
  50.     if (currentLocation.y + currentSize.height >= bounds.size.height)
  51.         currentLocation.y = bounds.size.height - currentSize.height;
  52.         
  53.     if (currentLocation.y <= bounds.origin.y)
  54.         currentLocation.y = 0;
  55.  
  56.     return self;
  57. }
  58.  
  59.  
  60. /* This handles bouncing the image- i.e. changing the move vector if needed
  61.  * when currentImage hits the edge of the screen/window. Note a similar kludge
  62.  * as in fixPosition that allows for the truncated AM/PM times.
  63.  */
  64. - bounceIfNeeded
  65. {
  66.     if (currentLocation.x <= bounds.origin.x)
  67.         moveVector.x= bounceMultiplier * randBetween(0.5, 1.5);
  68.         
  69.     if (militaryTime == NO && didShorten == YES) {
  70.         if (currentLocation.x + .9 * currentSize.width >= bounds.size.width)
  71.             moveVector.x= bounceMultiplier * (-1) * randBetween(0.5, 1.5);
  72.     }
  73.     else {
  74.         if (currentLocation.x + currentSize.width >= bounds.size.width)
  75.             moveVector.x= bounceMultiplier * (-1) * randBetween(0.5, 1.5);
  76.     }
  77.     
  78.     if (currentLocation.y <= bounds.origin.y)
  79.         moveVector.y= bounceMultiplier * randBetween(0.5, 1.5);
  80.         
  81.     if (currentLocation.y + currentSize.height >= bounds.size.height)
  82.         moveVector.y= bounceMultiplier * (-1) * randBetween(0.5, 1.5);
  83.  
  84.     return self;
  85. }
  86.  
  87.  
  88. - chooseColor
  89. {
  90.     float dr, dg, db;
  91.  
  92.     if ([Window defaultDepthLimit] == NX_TwoBitGrayDepth) {
  93.         PSsetgray(1.0);         /* yipee! */
  94.     }
  95.  
  96.     else {
  97.         dr= randBetween(-.1, .1); /* randomly move the red, green, and blue */
  98.         dg= randBetween(-.1, .1); /* components of the color around between */
  99.         db= randBetween(-.1, .1); /* 0 and 1. There is a minor bummer in that
  100.                                    * you only see the effect of the color
  101.                                    * change once a second, because it only
  102.                                    * draws the time into the bitmap when the
  103.                                    * time changes. Would be an easy modification
  104.                                    * to get it to redraw the bitmap more
  105.                                    * frequently, but its probably not worth
  106.                                    * the processor cycles. Depending on how this
  107.                                    * ends up looking in color (wish I knew!), it
  108.                                    * might be better to replace everything
  109.                                    * between the else and the PSsetrgbcolor
  110.                                    * with just a PSsetrgbcolor(), with constant
  111.                                    * arguments...
  112.                                    */
  113.  
  114.         currR += dr;    currG += dg;     currB += db;
  115.  
  116.         if (currR < 0.0) currR= 0.0;
  117.         if (currR > 1.0) currR= 1.0;
  118.         if (currG < 0.0) currG= 0.0;
  119.         if (currG > 1.0) currG= 1.0;
  120.         if (currB < 0.0) currB= 0.0;
  121.         if (currB > 1.0) currB= 1.0;
  122.         PSsetrgbcolor(currR, currG, currB);
  123.     }
  124.  
  125.     return self;
  126. }
  127.  
  128.  
  129.  
  130. - oneStep
  131. {
  132.     time_t tTime;
  133.     struct tm *currentTime;
  134.     
  135.     
  136.     /* if a new currentImage has been allocated, most likely the size of the
  137.      * clock or whether military time is being used or not has changed. Thus,
  138.      * it's a good idea to clear out the drawing area, so that no little
  139.      * remnants are left hanging around.
  140.      */
  141.     if (isNew == YES) {
  142.         PSsetgray(0.0);
  143.         NXRectFill(&bounds);
  144.         isNew= NO;
  145.     }
  146.  
  147.     currentLocation.x += moveVector.x;
  148.     currentLocation.y += moveVector.y;
  149.     [self bounceIfNeeded];
  150.     [self fixPosition];
  151.  
  152.     time(&tTime);
  153.     currentTime= localtime(&tTime);
  154.     
  155.     if (militaryTime == YES) 
  156.         strftime(theTime, 14, "%H:%M:%S", currentTime);
  157.     else {
  158.         strftime(theTime, 14, "%I:%M:%S %p", currentTime);
  159.         if (theTime[0] == '0') {
  160.             strcpy(theTime, theTime+1); /* take off the leading 0 */
  161.             didShorten= YES;
  162.         }
  163.         else
  164.             didShorten= NO;
  165.     }
  166.  
  167.     if (strcmp(theTime, lastTime) != 0) { /* if the time has changed... */
  168.         strcpy(lastTime, theTime);
  169.  
  170.         currentLocation.x += moveVector.x; /* move it one more time so it's not so jerky */
  171.         currentLocation.y += moveVector.y; /* when the time changes... */
  172.         [self bounceIfNeeded];
  173.         [self fixPosition];
  174.  
  175.         if ([currentImage lockFocus] == YES) { /* draw a new bitmap */
  176.             PSsetgray(0.0);
  177.             PSrectfill(0, 0, currentSize.width, currentSize.height);
  178.             [theFont set];
  179.  
  180.             PSsetgray(.333);    /* give it a little shadow */
  181.             PSmoveto(14, clockSize);
  182.             PSshow(theTime);
  183.  
  184.             PSmoveto(10, clockSize + 2);
  185.             [self chooseColor];
  186.             PSshow(theTime);
  187.             
  188.             [currentImage unlockFocus];
  189.         }
  190.     }
  191.  
  192.     [currentImage composite:NX_COPY toPoint:¤tLocation];
  193.  
  194.     usleep((1*1000000)/68);
  195.  
  196.     return self;
  197. }
  198.  
  199.  
  200. - (const char *)windowTitle
  201. {
  202.     return "Digital Clock";
  203. }
  204.  
  205.  
  206. - drawSelf:(const NXRect *)rects :(int)rectCount
  207. {
  208.     if (!rects || !rectCount) {
  209.         return self;
  210.     }
  211.  
  212.     PSsetgray(0);
  213.     NXRectFill(rects);
  214.  
  215.     return self;
  216. }
  217.  
  218.  
  219. - newClock
  220. {
  221.     *theTime= '\0';
  222.     *lastTime= '\0';
  223.  
  224.     theFont=  [Font newFont:"Times-Roman" size:clockSize];
  225.  
  226.     currR= currG= currB= .5;
  227.  
  228.     if (militaryTime == YES) {
  229.         currentSize.width= [theFont getWidthOf:"88:88:88"] + 20;
  230.     }
  231.     else {
  232.         currentSize.width= [theFont getWidthOf:"88:88:88 MM"] + 20;
  233.     }
  234.     
  235.     currentSize.height= clockSize + 20;
  236.     
  237.     [currentImage free];
  238.     currentImage= [[NXImage alloc] initSize:¤tSize];
  239.     [currentImage setFlipped:YES];
  240.     
  241.     isNew= YES;                 /* make sure oneStep knows it has a new
  242.                                  * bitmap to work with, and will clear
  243.                                  * the screen as needed...
  244.                                  */
  245.  
  246.     return self;
  247. }
  248.  
  249.  
  250.  
  251. - inspector:sender
  252. {
  253.     char buf[MAXPATHLEN];
  254.  
  255.     if (!sharedInspectorPanel) {
  256.         sprintf(buf,"%s/%s",[(BSThinker()) moduleDirectory:"DigitalClock"],
  257.                 "DigitalClock.nib");
  258.         [NXApp loadNibFile:buf owner:self withNames:NO];
  259.     }
  260.  
  261.     return sharedInspectorPanel;
  262. }
  263.  
  264.  
  265. - initFrame:(const NXRect *)frameRect
  266. {
  267.     [super initFrame:frameRect];
  268.  
  269.     currentLocation.x= 400.0;
  270.     currentLocation.y= 400.0;
  271.  
  272.     moveVector.x= randBetween(0.5, 1.5);
  273.     moveVector.y= randBetween(0.5, 1.5);
  274.     
  275.     didShorten= NO;
  276.     
  277.     [self inspector:self];      /* Must do this ourselves here instead of
  278.                                  * letting BackSpace take care of it because
  279.                                  * we use the connections defined in there
  280.                                  * down below where we sent setFloatValue, etc
  281.                                  * to the sliders and other controls.
  282.                                  */
  283.     
  284.     if (NXGetDefaultValue([NXApp appName], "digClkViewClockSize") == NULL) {
  285.         NXWriteDefault([NXApp appName], "digClkViewClockSize", "120.0");
  286.         NXWriteDefault([NXApp appName], "digClkViewBounceMult", "1.0");
  287.         NXWriteDefault([NXApp appName], "digClkViewMilitary", "NO");
  288.         clockSize= 120.0;
  289.         bounceMultiplier= 1.0;
  290.         militaryTime= NO;
  291.     }
  292.     
  293.     else {
  294.         clockSize= atof(NXGetDefaultValue([NXApp appName],
  295.                                           "digClkViewClockSize"));
  296.         if (clockSize < 10.0 || clockSize > 300.0)
  297.             clockSize= 120.0;
  298.         
  299.         bounceMultiplier= atof(NXGetDefaultValue([NXApp appName],
  300.                                                  "digClkViewBounceMult"));
  301.         if (bounceMultiplier < .1 || bounceMultiplier > 10.0)
  302.             bounceMultiplier= 1.0;
  303.  
  304.         if (strcmp(NXGetDefaultValue([NXApp appName], "digClkViewMilitary"),
  305.                    "YES") == 0) {
  306.             militaryTime= YES;
  307.         }
  308.         else militaryTime= NO;
  309.     }
  310.     
  311.     [sizeSlider setFloatValue:clockSize];
  312.     [sizeSlider update];
  313.     
  314.     [speedSlider setFloatValue:bounceMultiplier];
  315.     [speedSlider update];
  316.     
  317.     if (militaryTime == YES)
  318.         [militarySwitch setState:MILITARY];
  319.     else
  320.         [militarySwitch setState:AM_PM];
  321.     [militarySwitch update];
  322.  
  323.     [self newClock];
  324.     
  325.     return self;
  326. }
  327.  
  328.  
  329. - setClockSize:sender
  330. {
  331.     char temp[40];
  332.     
  333.     clockSize= [sender floatValue];
  334.     
  335.     sprintf(temp, "%f", clockSize);
  336.     NXWriteDefault([NXApp appName], "digClkViewClockSize", temp);
  337.     
  338.     [self newClock];
  339.  
  340.     return self;
  341. }
  342.  
  343.  
  344. - setBounceMultiplier:sender
  345. {
  346.     char temp[40];
  347.  
  348.     bounceMultiplier= [sender floatValue];
  349.      
  350.     sprintf(temp, "%f", bounceMultiplier);
  351.     NXWriteDefault([NXApp appName], "digClkViewBounceMult", temp);
  352.     
  353.     
  354.     /* update the moveVector as needed so that the changes aare reflected
  355.      * immediately, instead of forcing the user to wait until the clock
  356.      * bounces off of an edge to see the effect.. 
  357.      */
  358.     if (moveVector.x > 0) 
  359.         moveVector.x= bounceMultiplier * randBetween(0.5, 1.5);
  360.     else 
  361.         moveVector.x= (-1) * bounceMultiplier * randBetween(0.5, 1.5);
  362.     
  363.     if (moveVector.y > 0)
  364.         moveVector.y= bounceMultiplier * randBetween(0.5, 1.5);
  365.     else
  366.         moveVector.y= (-1) * bounceMultiplier * randBetween(0.5, 1.5);
  367.      
  368.     return self;
  369. }
  370.  
  371.  
  372. - setMilitaryTime:sender
  373. {
  374.     if ([sender state] == MILITARY) {
  375.         militaryTime= YES;
  376.         NXWriteDefault([NXApp appName], "digClkViewMilitary", "YES");
  377.     }
  378.     else {
  379.         militaryTime= NO;
  380.         NXWriteDefault([NXApp appName], "digClkViewMilitary", "NO");
  381.     }
  382.     
  383.     [self newClock];
  384.  
  385.     return self;
  386. }
  387.  
  388. @end
  389.  
  390.